package com.java.payweb.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.java.pay.entity.PaymentInfo;
import com.java.payweb.feign.PayFeign;
import com.java.payweb.service.CallbackService;
import com.java.payweb.unionpay.acp.sdk.AcpService;
import com.java.payweb.unionpay.acp.sdk.LogUtil;
import com.java.payweb.unionpay.acp.sdk.SDKConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @author jiangli
 * @since 2019/12/16 21:54
 */
@Service
@Slf4j
public class UnPayCallbackServiceImpl implements CallbackService {
	@Autowired
	private PayFeign payFeign;

	@Override
	public Map<String, String> sync(HttpServletRequest request) {
		LogUtil.writeLog("FrontRcvResponse前台接收报文返回开始");
		try {
			String encoding = request.getParameter(SDKConstants.param_encoding);
			return valideData(request, encoding);
		} catch (Exception e) {
			log.error("UnPayCallbackService##syn() ERROR:", e);
			return null;
		} finally {
			LogUtil.writeLog("FrontRcvResponse前台接收报文返回结束");
		}
	}

	@Override
	public String async(HttpServletRequest request) {
		LogUtil.writeLog("BackRcvResponse接收后台通知开始");

		String encoding = request.getParameter(SDKConstants.param_encoding);
		Map<String, String> valideData = valideData(request, encoding);
		// 重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
		if (!AcpService.validate(valideData, encoding)) {
			LogUtil.writeLog("验证签名结果[失败].");
			// 验签失败，需解决验签问题
			return "fail";
		}
		LogUtil.writeLog("验证签名结果[成功].");
		// 【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态

		String orderId = valideData.get("orderId"); // 获取后台通知的数据，其他字段也可用类似方式获取
		PaymentInfo paymentInfo = payFeign.getPayInfoByOrderId(orderId);
		if (paymentInfo == null) {
			return "fail";
		}
		//校验订单金额和回调中的支付金额
		Long settleAmt = Long.valueOf(valideData.get("settleAmt"));
		if (!paymentInfo.getPrice().equals(settleAmt)) {
			log.error("金额不正确,应付{},实付{}",paymentInfo.getPrice(),settleAmt);
			return "fail";
		}
		Integer state = paymentInfo.getState();
		// 1 支付成功
		if (state.equals(1)) {
			log.error("UnPayCallbackService异步通知async(),订单id:{},状态已经为已支付,不能在修改为已支付.", orderId);
			return "ok";
		}
		//第三方流水号
		paymentInfo.setPlatformOrderId(valideData.get("queryId"));
		paymentInfo.setUpdateTime(new Date());
		paymentInfo.setPaymessage(JSONUtil.toJsonStr(valideData));
		paymentInfo.setState(1);
		payFeign.updatePayInfo(paymentInfo);
		log.info("UnPayCallbackService异步通知async(),订单id:{},状态已经为已支付,修改成功。", orderId);
		// 返回给银联服务器http 200 状态码 返回ok,不是success
		return "ok";
	}

	/**
	 * 组装参数
	 */
	private static Map<String, String> valideData(final HttpServletRequest req, String encoding) {
		// 获取银联通知服务器发送的后台通知参数
		Map<String, String> reqParam = getAllRequestParam(req);

		LogUtil.printRequestLog(reqParam);

		Map<String, String> valideData = null;
		if (CollUtil.isNotEmpty(reqParam)) {
			Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
			valideData = new HashMap<>(reqParam.size());
			while (it.hasNext()) {
				Map.Entry<String, String> entry = it.next();
				String key = entry.getKey();
				String value = entry.getValue();
				try {
					value = new String(value.getBytes(encoding), encoding);
				} catch (Exception e) {
					e.printStackTrace();
				}
				valideData.put(key, value);
			}
		}
		return valideData;
	}

	/**
	 * 获取请求参数中所有的信息
	 */
	private static Map<String, String> getAllRequestParam(final HttpServletRequest request) {
		Map<String, String> res = new HashMap<String, String>();
		Enumeration<?> temp = request.getParameterNames();
		if (null != temp) {
			while (temp.hasMoreElements()) {
				String en = (String) temp.nextElement();
				String value = request.getParameter(en);
				res.put(en, value);
				// 在报文上送时，如果字段的值为空，则不上送<下面的处理为在获取所有参数数据时，判断若值为空，则删除这个字段>
				if (res.get(en) == null || "".equals(res.get(en))) {
					// System.out.println("======为空的字段名===="+en);
					res.remove(en);
				}
			}
		}
		return res;
	}
}
